package com.tangsm.spring.boot.validation.aspect;

import com.tangsm.spring.boot.validation.domain.vo.Result;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;

import java.util.List;

/**
 * 参数验证借助AOP切面实现案例
 * <p>依赖 spring-boot-starter-aop jar包</p>
 * <p>这种方式可以减少controller层校验的代码，校验逻辑统一处理，更高效。</p>
 * <p>execution 表达式参考：https://blog.csdn.net/chenHaiJaheike/article/details/91908367</p>
 * @author tangsm
 */
@Aspect
@Component
public class ControllerValidatorAspect {

    private static final Logger log = LoggerFactory.getLogger(ControllerValidatorAspect.class);

    /**
     * AOP全局拦截校验器
     * @param bindingResult 绑定结果的通用接口
     * @return 统一响应信息
     */
    @Around("execution(public com.tangsm.spring.boot.validation.domain.vo.Result com.tangsm.spring.boot.validation.controller.ValidDemoController.aopAdd(..)) && args(.., bindingResult)")
    public Result<?> doAround(ProceedingJoinPoint pjp, BindingResult bindingResult) {
        // 获取与字段相关的所有错误
        List<FieldError> allErrors = bindingResult.getFieldErrors();

        String errorMsg = "";

        // 存在错误拼接所有错误信息
        if(!CollectionUtils.isEmpty(allErrors)) {
            errorMsg = allErrors
                    .stream()
                    .map(error -> String.format("%s: %s", error.getField(), error.getDefaultMessage()))
                    .reduce((x, y) -> String.format("%s; %s", x, y))
                    .orElse("参数有误");

            log.debug("数据校验结果：{}", errorMsg);
        }

        if (!"".equals(errorMsg)) {
            return Result.verifyError(errorMsg);
        }

        Object obj = null;
        //执行目标方法
        try {
            obj = pjp.proceed();
        } catch (Throwable e) {
            log.error("执行目标方法方法异常", e);
        }
        return (Result<?>) obj;
    }
}
